using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using ZoneFiveSoftware.Common.Data;
using ZoneFiveSoftware.Common.Data.Fitness;
using ZoneFiveSoftware.Common.Data.Measurement;
using FilteredStatistics.Common;
using FilteredStatistics.Common.Controller;
using FilteredStatistics.Controller;
using FilteredStatistics.View;

namespace FilteredStatistics.Data
{
    class CustomDistanceLapsFilterCriteria : FilterCriteriaFacade, ISplitsCriteria
    {
        public CustomDistanceLapsFilterCriteria(IActivity activity,
                                                float distance,
                                                float startOffset,
                                                Length.Units distanceUnit)
        {
            m_ActivityCallbackHelper = new ActivityDataChangedHelper(activity);
            m_ActivityCallbackHelper.PropertyChanged += new PropertyChangedEventHandler(OnActivityDataChanged);

            Init(activity, distance, startOffset, distanceUnit);
        }

#region FilterCriteriaFacade members

        public override Guid ReferenceId
        {
            get { return GUIDs.SplitsCriteriaGuid; }
        }

        public override IActivity Activity
        {
            set
            {
                if (m_Activity != value)
                {
                    m_Activity = value;
                    m_ActivityCallbackHelper.Activity = m_Activity;

                    CreateSplitsNamedZonesList();
                }
            }
        }

        public override String DisplayName
        {
            get { return PluginView.GetLocalizedString("CustomDistanceLapsText"); }
        }

        public override bool IsTemplateOnly
        {
            get { return false; }
        }

        public override bool IsMainViewOnly
        {
            get { return false; }
        }

        public override List<object> NamedZones
        {
            get { return m_SplitsNamedZones; }
        }

        public override bool IsSerializable
        {
            get { return false; }
        }

        public override UInt16 DataVersion
        {
            get { return Constants.CurrentVersion.VersionNumber; }
        }

        public override object DeserializeCriteria(Stream stream, UInt16 dataVersion)
        {
            Debug.Assert(false);

            return null;
        }

        public override void SerializeCriteria(Stream stream)
        {
            Debug.Assert(false);
        }

        public override object TemplateCompatibleCriteria
        {
            get
            {
                float maxDistance = m_LapDistance * NamedZones.Count;

                return new TemplateCustomDistanceLapsFilterCriteria(maxDistance, m_LapDistance,
                                                                    m_StartOffset, m_DistanceUnit);
            }
        }

#endregion

#region ISplitsCriteria Members

        public SplitsCriteriaType SplitsType
        {
            get { return SplitsCriteriaType.CustomDistance; }
        }

#endregion

#region IPluginSerializable members

        public override void Serialize(Stream stream)
        {
            // We should never end up with this in a template
            Debug.Assert(false);
        }

#endregion

        private void OnViewCurrentActivityChanged(IActivity currentActivity)
        {
            Activity = currentActivity;
        }

        private void OnActivityDataChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Activity.Duration")
            {
                CreateSplitsNamedZonesList();
            }
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            CustomDistanceLapsFilterCriteria criteria = obj as CustomDistanceLapsFilterCriteria;
            SplitsPlaceholderFilterCriteria criteriaPlaceholder = obj as SplitsPlaceholderFilterCriteria;

            if (criteriaPlaceholder != null)
            {
                return true;
            }
            else if (criteria != null)
            {
                return criteria.LapDistance == LapDistance &&
                       criteria.StartOffset == StartOffset &&
                       criteria.DistanceUnits == DistanceUnits;
            }

            return base.Equals(obj);
        }

        private void CreateSplitsNamedZonesList()
        {
            if (m_Activity != null)
            {
                ActivityInfo activityInfo = ActivityInfoCache.Instance.GetInfo(m_Activity);

                if (activityInfo.MovingDistanceMetersTrack != null)
                {
                    float maximumDistance = (float)Length.Convert(activityInfo.MovingDistanceMetersTrack.Max, Length.Units.Meter, m_DistanceUnit);
                    float lapStartDistance = m_StartOffset;
                    float lapEndDistance = lapStartDistance + m_LapDistance;
                    int lapIndex = 0;

                    // Create/update a named zone for each distance interval
                    while (lapStartDistance < maximumDistance)
                    {
                        DistanceLapNamedZone currentNamedZone;

                        if(m_SplitsNamedZones.Count > lapIndex)
                        {
                            // Reuse the already created named zone
                            currentNamedZone = m_SplitsNamedZones[lapIndex] as DistanceLapNamedZone;
                            currentNamedZone.Init(m_Activity, lapStartDistance, Math.Min(maximumDistance, lapEndDistance), m_DistanceUnit);
                        }
                        else
                        {
                            // Create a new one
                            currentNamedZone = new DistanceLapNamedZone(m_Activity, lapIndex, lapStartDistance, Math.Min(maximumDistance, lapEndDistance), m_DistanceUnit);
                            m_SplitsNamedZones.Add(currentNamedZone);
                        }

                        lapStartDistance = lapEndDistance;
                        lapEndDistance += m_LapDistance;
                        lapIndex++;
                    }

                    // Remove excess named zones if required
                    if (lapIndex < m_SplitsNamedZones.Count)
                    {
                        m_SplitsNamedZones.RemoveRange(lapIndex, m_SplitsNamedZones.Count - lapIndex);
                    }
                }

                TriggerNamedZonesListChanged();
            }
        }

        public void Init(IActivity activity, float distance, float offset, Length.Units distanceUnit)
        {
            m_LapDistance = distance;
            m_StartOffset = offset;
            m_DistanceUnit = distanceUnit;
            Activity = activity;
        }

        public float LapDistance
        {
            get { return m_LapDistance; }
        }

        public float StartOffset
        {
            get { return m_StartOffset; }
        }

        public Length.Units DistanceUnits
        {
            get { return m_DistanceUnit; }
        }

        private IActivity m_Activity = null;
        private ActivityDataChangedHelper m_ActivityCallbackHelper = null;
        private float m_LapDistance;
        private float m_StartOffset;
        private Length.Units m_DistanceUnit;
        private List<object> m_SplitsNamedZones = new List<object>();
    }
}
